﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Text;
using System.IO;

namespace ANLP.NLinq
{
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            InitializeComponent();
            {
                InitializeComponent();
                StringBuilder sb = new StringBuilder();
                ANLP.Parser.ParserGenerator.Generate(new StringReader(@"
%Namespace Evaluant.NLinq
%Visibility public
%ExpressionType Expression
%using System, System.Text
%using Evaluant.NLinq.Expressions
%using ANLP
%token BracketOpen, BracketClose, BraceOpen, BraceClose, ParenthesisOpen, ParenthesisClose
%token QuestionMark, Colon, BooleanOr, BooleanAnd, Not, COMMA
%token Plus, Minus, Multiply, Divide, Modulo
%token Equals, NotEquals, LessThan, LessThanOrEquals, GreaterThan, GreaterThanOrEquals
%token Dot, New, Equal, Arrow
%token INTEGER, FLOAT, STRING, ID, TRUE, FALSE, DATETIME
%token FROM, IN, INTO, WHERE, LET, JOIN, ON, EQUALS, ORDERBY, SELECT, GROUP, BY
%token ASCENDING, DESCENDING

%%

private BinaryExpressionType type;
private const char BS = '\\';

private string extractString(string text) {
    
    StringBuilder sb = new StringBuilder(text);
    int startIndex = 1; // Skip initial quote
    int slashIndex = -1;

    while ((slashIndex = sb.ToString().IndexOf(BS, startIndex)) != -1)
    {
        char escapeType = sb[slashIndex + 1];
        switch (escapeType)
        {
            case 'u':
              string hcode = String.Concat(sb[slashIndex+4], sb[slashIndex+5]);
              string lcode = String.Concat(sb[slashIndex+2], sb[slashIndex+3]);
              char[] unicodeChar = Encoding.Unicode.GetChars(new byte[] { System.Convert.ToByte(hcode, 16), System.Convert.ToByte(lcode, 16)} );
              // Should be an array of 1 item
              sb.Remove(slashIndex, 6).Insert(slashIndex, unicodeChar); 
              break;
            case 'n': sb.Remove(slashIndex, 2).Insert(slashIndex, new char[]{ '\n' }); break;
            case 'r': sb.Remove(slashIndex, 2).Insert(slashIndex, new char[]{ '\r' }); break;
            case 't': sb.Remove(slashIndex, 2).Insert(slashIndex, new char[]{ '\t' }); break;
            case '\'': sb.Remove(slashIndex, 2).Insert(slashIndex, new char[]{ '\'' }); break;
            case '\\': sb.Remove(slashIndex, 2).Insert(slashIndex, new char[]{ '\\' }); break;
            default: throw new InvalidSyntaxException(""Unvalid escape sequence: "" + escapeType);
        }

        startIndex = slashIndex + 1;

    }

    sb.Remove(0, 1);
    sb.Remove(sb.Length - 1, 1);

    return sb.ToString();
}


%%

linqExpression : expression {$$.Value= $0.Value; } ;

expression
	:	expressionItem { $$.Value= $0.Value; } IndexerOpt { if($1.Value!=null) { ((Indexer)$1.Value).InnerExpression=$$.Value; $$.Value=$1.Value; } }
	;

IndexerOpt
    : BracketOpen expression BracketClose { $$.Value= new Indexer(null, $2.Value); }
    |
    ;
	
expressionItem
	:	conditionalExpression { $$.Value= $0.Value; } ternaryExpression
	;

ternaryExpression
    : QuestionMark conditionalExpression Colon conditionalExpression { $$.Value= new TernaryExpression($0.Value, $2.Value, $4.Value); }
    |
    ;

conditionalExpression
	:	booleanAndExpression conditionalExpressionOpt { if($1.Value!=null) { $$.Value=$1.Value; ((BinaryExpression)$$.Value).LeftExpression=$0.Value; } else { $$.Value=$0.Value; } }
	;

conditionalExpressionOpt
    : conditionalExpression_2 { $$.Value=$0.Value; }
    |
    ;
conditionalExpression_2
    : 	BooleanOr booleanAndExpression { $$.Value= new BinaryExpression(BinaryExpressionType.Or, null, $1.Value); } 
    ;

booleanAndExpression
	:	equalityExpression { $$.Value= $0.Value; }	booleanAndExpressionOpt { if($1.Value!=null) { $$.Value=$1.Value; ((BinaryExpression)$$.Value).LeftExpression=$0.Value; } }
    ;

booleanAndExpressionOpt
    : booleanAndExpression_2 { $$.Value=$0.Value; }
    |
    ;
booleanAndExpression_2
    : BooleanAnd equalityExpression { $$.Value = new BinaryExpression(BinaryExpressionType.And, null, $1.Value); } 
	;

equalityExpression
    : relationalExpression equalityExpressionOpt { if($1.Value==null) { $$.Value=$0.Value; } else { ((BinaryExpression)$1.Value).LeftExpression=$0.Value; $$.Value=$1.Value; } }
	;

equalityExpressionOpt
    : equalityExpression_2  { $$.Value=$0.Value; }
    |
    ;
equalityExpression_2 
    : Equals relationalExpression { $$.Value = new BinaryExpression(BinaryExpressionType.Equal, null, $1.Value); } 
	| NotEquals relationalExpression { $$.Value = new BinaryExpression(BinaryExpressionType.NotEqual, null, $1.Value); } 
    ;
			
	
relationalExpression	
    : additiveExpression relationalExpressionOpt { if($1.Value==null) { $$.Value=$0.Value; } else { ((BinaryExpression)$1.Value).LeftExpression=$0.Value; $$.Value=$1.Value; } }
	;

relationalExpressionOpt
    : relationalExpression_2  { $$.Value=$0.Value; }
    |
    ;

relationalExpression_2 
    : LessThan additiveExpression { $$.Value= new BinaryExpression(BinaryExpressionType.Lesser, null, $1.Value); } 
	| LessThanOrEquals additiveExpression { $$.Value= new BinaryExpression(BinaryExpressionType.LesserOrEqual, null, $1.Value); } 
	| GreaterThan additiveExpression { $$.Value= new BinaryExpression(BinaryExpressionType.Greater, null, $1.Value); } 
	| GreaterThanOrEquals additiveExpression { $$.Value= new BinaryExpression(BinaryExpressionType.GreaterOrEqual, null, $1.Value); }
    ;

additiveExpression 	
    : multiplicativeExpression additiveExpressionOpt { if($1.Value==null) { $$.Value=$0.Value; } else { ((BinaryExpression)$1.Value).LeftExpression=$0.Value; $$.Value=$1.Value; } }
	;

additiveExpressionOpt
    : additiveExpression_2  { $$.Value=$0.Value; }
    |
    ;

additiveExpression_2 
    : Plus multiplicativeExpression { $$.Value= new BinaryExpression(BinaryExpressionType.Plus, null, $1.Value); } 
	| Minus multiplicativeExpression { $$.Value= new BinaryExpression(BinaryExpressionType.Minus, null, $1.Value); } 
    ;

multiplicativeExpression 
    : unaryExpression { $$.Value = $0.Value; } multiplicativeExpressionOpt { if($1.Value!=null){ $$.Value =$1.Value; ((BinaryExpression)$$.Value).LeftExpression=$0.Value; } }
	;

multiplicativeExpressionOpt
    : multiplicativeExpression_2 { $$.Value=$0.Value; }
    |
    ;

multiplicativeExpression_2
    : Multiply { type = BinaryExpressionType.Times; } unaryExpression { $$.Value= new BinaryExpression(type, null, $1.Value); }
	| Divide { type = BinaryExpressionType.Div; } unaryExpression { $$.Value= new BinaryExpression(type, null, $1.Value); }
	| Modulo { type = BinaryExpressionType.Modulo; } unaryExpression { $$.Value= new BinaryExpression(type, null, $1.Value); }
    ;

	
unaryExpression
    :	Not memberExpression { $$.Value= new UnaryExpression(UnaryExpressionType.Not, $1.Value); }
    |	Minus memberExpression { $$.Value= new UnaryExpression(UnaryExpressionType.Negate, $1.Value); }
	|	memberExpression { $$.Value = $0.Value; }
   	;

memberExpression 
    :	primaryExpression { $$.Value =$0.Value; } memberExpressionOpt { if($1.Value!=null) { FindDeepest(((MemberExpression)$1.Value)).Previous=$0.Value; $$.Value=$1.Value; } }
	;
	
memberExpressionOpt
    : memberExpression_2 {$$.Value=$0.Value; /* Test */ }
    |
    ;

memberExpression_2 
    : memberExpression_2 { $$.Value=$0.Value;  }
    | propertyExpression { ((MemberExpression)$0.Value).Previous=$$.Value; $$.Value =$0.Value; }
    ;
    

propertyExpression:
	Dot primaryExpression { $$.Value= new MemberExpression($1.Value, null); }
    ;
		
primaryExpression
	:	value		                        { $$.Value= $0.Value; }
	|	ParenthesisOpen expression ParenthesisClose 	{ $$.Value =$1.Value; }
	|	newExpression		                { $$.Value= $0.Value; }
	|	methodCall		{ $$.Value= $0.Value; }
	|	identifier {$$.Value= ((Identifier)$0.Value).Text == ""null"" ? new ValueExpression(null, TypeCode.Object) : (Expression) $0.Value; } 
	| 	queryExpression		{ $$.Value= $0.Value; }
	;

value
	: 	INTEGER		{ $$.Value= new ValueExpression(int.Parse($0.Text), TypeCode.Int32); }
	|	FLOAT		{ $$.Value= new ValueExpression(float.Parse($0.Text, System.Globalization.NumberFormatInfo.CurrentInfo), TypeCode.Single); }
	|	STRING		{ $$.Value= new ValueExpression(extractString($0.Text), TypeCode.String); }
	|	boolean		{ $$.Value= new ValueExpression($0.Text, TypeCode.Boolean); }
    |   lambdaExpression { $$.Value=$0.Value; }
	;

methodCall	:	identifier arguments { $$.Value= new MethodCall($0.Text, $1.Value); }
	;

lambdaExpression
    : ParenthesisOpen identifiers ParenthesisClose Arrow lambdaBody { $$.Value = new LambdaExpression((BlockExpression)$1.Value, $4.Value); }
    | identifier Arrow lambdaBody { $$.Value=new LambdaExpression($0.Value,$2.Value); }
    ;

lambdaBody
    : BraceOpen expression BraceClose
    | expression { $$.Value=$0.Value; }
    ;

identifier 
	: 	ID { $$.Value = new Identifier($0.Text); }
	;

identifiers : identifiers
    | COMMA identifier { ((BlockExpression)$$.Value).Add($1.Value); }
    | identifier { $$.Value=new BlockExpression($0.Value); }
    ;

newExpression
	:	New typeOpt BraceOpen typeInits BraceClose
			{$$.Value = new AnonymousNew($1.Text, ((AnonymousParameterList)$3.Value).ToArray()); }
	| 	New type arguments  {$$.Value = new TypedNew($1.Text, ((BlockExpression)$2.Value).ToArray()); }
	;			

typeInits: typeInits 
    | COMMA typeInit { ((AnonymousParameterList)$$.Value).Add((AnonymousParameter)$1.Value); }
    | typeInit { $$.Value=new AnonymousParameterList((AnonymousParameter)$0.Value); }
    ;
	
typeInit : identifier Equal expression { $$.Value=new AnonymousParameter($0.Text, $2.Value); }
    | expression { $$.Value=new AnonymousParameter((string)null,$0.Value); }
    ;

typeOpt:
    type
    |
    ;

queryExpression
	: 	fromClause queryBody { $$.Value= new QueryExpression((FromClause)$0.Value, (QueryBody)$1.Value); }
	;
	
fromClause
	:	FROM type identifier IN expression { $$.Value= new FromClause($1.Text, (Identifier)$2.Value, $4.Value); }
    |   FROM identifier IN expression { $$.Value= new FromClause(null, (Identifier)$1.Value, $3.Value); }
	;
		
queryBody
	:	queryBodyClauses selectOrGroupClause queryContinuationOpt	{ $$.Value= new QueryBody((ClauseList)$0.Value, (SelectOrGroupClause)$1.Value, (QueryContinuation)$2.Value); }
    |   selectOrGroupClause queryContinuationOpt	{ $$.Value= new QueryBody(null, (SelectOrGroupClause)$0.Value, (QueryContinuation)$1.Value); }
	;

queryBodyClauses 
    : queryBodyClauses
    | queryBodyClause { if($$.Value==null) { $$.Value=new ClauseList(); } ((ClauseList)$$.Value).Add((QueryBodyClause)$0.Value); }
    ;
	
queryBodyClause
	:	fromClause 	{ $$.Value= $0.Value; }
	| 	letClause 	{ $$.Value= $0.Value; }
	| 	whereClause 	{ $$.Value= $0.Value; }
	| 	joinClause 	{ $$.Value= $0.Value; }
	| 	orderByClauses	{ $$.Value= $0.Value; }
	;


selectOrGroupClause
    : selectClause { $$.Value =$0.Value; }
    | groupClause { $$.Value =$0.Value; }
    ;


queryContinuationOpt : queryContinuation { $$.Value=$0.Value; }
    |
    ;

queryContinuation
	: 	INTO identifier queryBody { $$.Value= new QueryContinuation((Identifier)$1.Value, (QueryBody)$2.Value); }
	;
		
whereClause
	:	WHERE expression { $$.Value= new WhereClause($1.Value); }
	;
letClause 
	:	LET identifier Equal expression { $$.Value= new LetClause((Identifier)$1.Value, $3.Value); }
	;
	
joinClause
	:	JOIN type identifier IN expression ON expression EQUALS expression intoOpt
	{ $$.Value= new JoinClause($1.Text, (Identifier)$2.Value, $4.Value, $6.Value, $8.Value, (Identifier)$9.Value); }
	|	JOIN identifier IN expression ON expression EQUALS expression intoOpt
	{ $$.Value= new JoinClause(null, (Identifier)$1.Value, $3.Value, $5.Value, $7.Value, (Identifier)$8.Value); }
	;

intoOpt : into { $$.Value =$0.Value;}
    |
    ;

into: INTO identifier { $$.Value =$1.Value; };
		
orderByClause
	:	ORDERBY expression orderOpt { $$.Value = new OrderByCriteria($1.Value, $2.Text == null || $2.Text == ""ascending"");} 
	;
	
orderByClauses : orderByClauses 
    | COMMA orderByClause { ((OrderByClause)$$.Value).Add((OrderByCriteria)$2.Value); }
    | orderByClause { $$.Value = new OrderByClause((OrderByCriteria)$0.Value);}
    ;

orderOpt : order { $$.Value = $0.Value; }
    |
    ;

order : ASCENDING { $$.Value = $0.Value;}
    | DESCENDING { $$.Value = $0.Value;}
    ;

selectClause
	:	SELECT expression { $$.Value= new SelectClause($1.Value); }
	;

groupClause
	:	GROUP identifier BY expression { $$.Value= new GroupClause((Identifier)$1.Value, $3.Value); }
	;
	
type
	: identifier Dot type
    | identifier
	;

expressions : expressions  
    | COMMA { if(!($$.Value is BlockExpression)) {$$.Value =new BlockExpression($$.Value);} } expression { ((BlockExpression)$$.Value).Add($1.Value); }
    | expression { $$.Value =$0.Value; }
    ;

	
arguments :	ParenthesisOpen expressionsOpt ParenthesisClose {$$.Value= $1.Value;}
	;			

expressionsOpt : expressions { $$.Value =$0.Value; }
    |
    ;

boolean
	:	TRUE
	|	FALSE
	;

"), new StringWriter(sb));
                parserOutput.Text = sb.ToString();
                sb = new StringBuilder();
                ANLP.Lexer.LexerGenerator.Generate(new StringReader(@"%Namespace Evaluant.NLinq
%Visibility public
[A-Za-z_@][A-Za-z0-9_]*        { return (int)Tokens.ID; }
\[        { return (int)Tokens.BracketOpen; }
\]        { return (int)Tokens.BracketClose; }
\{        { return (int)Tokens.BraceOpen; }
\}        { return (int)Tokens.BraceClose; }
\(        { return (int)Tokens.ParenthesisOpen; }
\)        { return (int)Tokens.ParenthesisClose; }
\?        { return (int)Tokens.QuestionMark; }
:        { return (int)Tokens.Colon; }
\|\|        { return (int)Tokens.BooleanOr; }
&&        { return (int)Tokens.BooleanAnd; }
!=        { return (int)Tokens.NotEquals; }
!        { return (int)Tokens.Not; }
,        { return (int)Tokens.COMMA; }
\+        { return (int)Tokens.Plus; }
\-        { return (int)Tokens.Minus; }
\*        { return (int)Tokens.Multiply; }
/        { return (int)Tokens.Divide; }
\%        { return (int)Tokens.Modulo; }
==        { return (int)Tokens.Equals; }
=>          { return (int)Tokens.Arrow; }
<=        { return (int)Tokens.LessThanOrEquals; }
<        { return (int)Tokens.LessThan; }
>=        { return (int)Tokens.GreaterThanOrEquals; }
>        { return (int)Tokens.GreaterThan; }
\.        { return (int)Tokens.Dot; }
new        { return (int)Tokens.New; }
=        { return (int)Tokens.Equal; }
[0-9]+       { return (int)Tokens.INTEGER; }
[0-9]*\.[0-9]+        { return (int)Tokens.FLOAT; }
'([^\\']|\\[^']|\\')*'        { return (int)Tokens.STRING; }
true        { return (int)Tokens.TRUE; }
false        { return (int)Tokens.FALSE; }
DateTime        { return (int)Tokens.DATETIME; }
from        { return (int)Tokens.FROM; }
in        { return (int)Tokens.IN; }
into        { return (int)Tokens.INTO; }
where        { return (int)Tokens.WHERE; }
let        { return (int)Tokens.LET; }
join        { return (int)Tokens.JOIN; }
on        { return (int)Tokens.ON; }
equals        { return (int)Tokens.EQUALS; }
orderby        { return (int)Tokens.ORDERBY; }
select        { return (int)Tokens.SELECT; }
group        { return (int)Tokens.GROUP; }
by        { return (int)Tokens.BY; }
ascending        { return (int)Tokens.ASCENDING; }
descending        { return (int)Tokens.DESCENDING; }
[ \t\n\r\f\v] { return (int)Tokens.Whitespace; }
"), new StringWriter(sb));
                lexerOutput.Text = sb.ToString();

            }
        }
    }
}
